package org.fing.edu.uy.esbadp.action.sync;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.fing.edu.uy.esbadp.assertion.AssertProperty;
import org.fing.edu.uy.esbadp.constants.SoapConstants;
import org.fing.edu.uy.esbadp.core.AdpFlowConstants;
import org.fing.edu.uy.esbadp.core.AdpFlowInterface;
import org.fing.edu.uy.esbadp.core.AdpFlowSYNC;
import org.fing.edu.uy.esbadp.core.AdpFlowTree;
import org.fing.edu.uy.esbadp.core.entity.GenericTreeNode;
import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.fing.edu.uy.esbadp.modules.cache.CacheKeyGenerator;
import org.fing.edu.uy.esbadp.modules.cache.CacheManagerFactory;
import org.fing.edu.uy.esbadp.utils.SoapExtraction;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.annotation.Process;
import org.jboss.soa.esb.addressing.Call;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.client.ServiceInvoker;
import org.jboss.soa.esb.http.HttpHeader;
import org.jboss.soa.esb.http.HttpRequest;
import org.jboss.soa.esb.message.Message;

public class SyncAction {
	
	private static java.util.logging.Logger logger = Logger.getLogger(SyncAction.class.getSimpleName());
	
	private ServiceInvoker serviceInvoker;
	private long timeout;
	private boolean failOnException;

	public SyncAction() {
        timeout = 30000L;
        failOnException = true;
	}

	/**
	 * @param message
	 */
	@Process
	public void process(Message message) throws ActionProcessingException {
		if(logger.isLoggable(LoggerADP.DEBUG_LEVEL)){
			logger.log(LoggerADP.DEBUG_LEVEL, "Process Action -> {0}", this.getClass().getName());
		}	
		
		AssertArgument.isNotNull(message, "message");
		AssertProperty.isNotNull(message, AdpFlowConstants.PropertyFlowServices);
		
		Call call = message.getHeader().getCall();
        EPR faultTo = call.getFaultTo();
        EPR replyTo = call.getReplyTo();

		//se obtiene tree de propiedades
		AdpFlowTree adpFlowTree = (AdpFlowTree) message.getProperties().getProperty(AdpFlowConstants.PropertyFlowServices);
			
		//invoca al siguiente servicio segun el treeFlow
		GenericTreeNode<AdpFlowInterface> treeNode = adpFlowTree.getCurrent();
		AdpFlowSYNC adpService = (AdpFlowSYNC)treeNode.getData();
		String serviceCategory = adpService.getInvokeSyncCategoryName();
		String serviceName = adpService.getInvokeSyncName();
		changeHeaderMessage(message, (adpService.getServiceId() != adpFlowTree.getServiceID()));

        try {
        	String messageRequestKey = null;
        	
        	if(adpService.isCacheable()){
        		//genero el key antes que el mensaje cambie.
        		messageRequestKey = CacheKeyGenerator.keyGenerator(message);	
        	}
        	
        	serviceInvoker = new ServiceInvoker(serviceCategory, serviceName);
            call.setFaultTo(null);
            call.setReplyTo(null);
                                    
            message.getProperties().remove(AdpFlowConstants.PropertyFlowServices);
            //Delivery Message to service proxy sync
            message = serviceInvoker.deliverSync(message, timeout);
            
            String serviveEndpoint = SoapExtraction.findElementInBody(message, SoapConstants.SOAP_FAULT_QNAME);    		
            
            if(adpService.isCacheable() && serviveEndpoint == null){ 
            	if(logger.isLoggable(LoggerADP.DEBUG_LEVEL)){
        			logger.log(LoggerADP.DEBUG_LEVEL, "Message chached");
        		}
            	CacheManagerFactory.getCacheManager().addMessageToCache(serviceCategory,serviceName,messageRequestKey,message.getBody(),adpService.getTtl(),adpService.getTtidle());
            }            
            
            //restauro los call
            call.setFaultTo(faultTo);
            call.setReplyTo(replyTo);
    		//como esta accion no tiene que decidir el proximo nodo es el primer hijo
    		GenericTreeNode<AdpFlowInterface> nextCurrent = treeNode.getChildren().size() > 0 ? treeNode.getChildAt(0) : null;
    		adpFlowTree.setCurrent(nextCurrent);
            message.getProperties().setProperty(AdpFlowConstants.PropertyFlowServices, adpFlowTree);
        } catch (Exception ex) {
            call.setFaultTo(faultTo);
            call.setReplyTo(replyTo);

            if (failOnException) {
                throw new ActionProcessingException("Error delivering message to service '" + serviceInvoker.getService() + "'", ex);
            } else {
            	if (logger.isLoggable(Level.WARNING)){
    				logger.log(Level.WARNING, "Exception during deliverSync().  Action configured to continue.", ex);
    			}
            }
        }
	}
	
	//Remove soapaction from header and wsa:To in equivalent services
	private void changeHeaderMessage(Message message, boolean equivalentService){
		HttpRequest request = HttpRequest.getRequest(message);
		request.getHeaders().remove(request.getHeader("soapaction"));
		request.getHeaders().add(new HttpHeader("soapaction", "\"\""));
		message.getProperties().setProperty("soapaction", "\"\"");
		if(equivalentService){
			String name = message.getBody().getNames()[0];
			message.getBody().add(name, message.getBody().get(name).toString().replaceAll("<\\w*:Action>.*</\\w*:Action>", ""));
		}
	}

}
